
#include "cmd.h"
#include "stdio.h"
#include "string.h"
#include "usart.h"

cmd_tbl_t * __lx_mcu_cmd_start = NULL;
cmd_tbl_t * __lx_mcu_cmd_end = NULL;

char console_buffer[MAX_CMD_LINE_LEN + 1];
static const char erase_seq[] = "\b \b";


cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
{
	cmd_tbl_t *cmdtp;
	cmd_tbl_t *cmdtp_temp = table;
	int len;
	int n_found = 0;
	
#ifdef CLI_DEBUG
	printf("cmd=%s,table_len=%d\n",cmd,table_len);
#endif
	
	if (!cmd)
		return NULL;
	
	len =  strlen(cmd);
	
	for (cmdtp = table; cmdtp != table + table_len; cmdtp++) 
	{
		if (strncmp (cmd, cmdtp->name, len) == 0) 
		{
			if (len == strlen(cmdtp->name))
				return cmdtp;   /* full match */
			
			cmdtp_temp = cmdtp;
			n_found++;
		}
	}
	
	return (1 == n_found) ? cmdtp_temp : NULL;
}

int cmd_usage(cmd_tbl_t *cmdtp)
{
	printf("%s  - %s\r\n", cmdtp->name, cmdtp->usage);
	
	if (cmdtp->help)
		printf("%s\r\n",cmdtp->help);
	
	return 1;
}

int readline_into_buffer(const char *const prompt, char * buffer);

/**
  * @brief  read cli string to buffer.          
  * @note   None        
  * @param  cli prompt
  * @retval 
  */
int lynxi_mcu_readline(const char *const prompt)
{
	console_buffer[0] = '\0';
	
	return readline_into_buffer(prompt, console_buffer);
}

/**
  * @brief  read cli string to buffer.          
  * @note   None        
  * @param  cli prompt
  * @retval 
  */
int readline_into_buffer(const char *const prompt, char * buffer)
{
	char *p = buffer;
	char *p_buf = buffer;
	int count = 0; /* buffer index */
	uint8_t ch;
    uint16_t len = 0;
    uint16_t i = 0;
	
	if (prompt)
		printf("%s",prompt);
	
    UART_RX_DMA_Status_Clear(); //clear data flag
    UART_RX_DMA_Enable();  //enable next dma date transfer
	/* infinite loop until receive enter or CTRL + C*/
	for(;;)
	{
		if (UART_RX_DMA_Status() & UART_RX_STAT_MASK) //receive data
        {
            len = UART_RX_DMA_Status() & ~UART_RX_STAT_MASK;
            
            if (len > 0)
            {
                for (i = 0; i < len; i++)
                {
                    ch = UART_RX_BUF[i];
                    switch( ch )
                    {
                        case '\r':    /* Enter (CR or LR)*/
                        case '\n':
                            *p = '\0';
                            printf("\r\n");
                            return (p - p_buf);
                        
                        case '\0':    /* nul */
                            continue;
                        
                        case 0x03:  /* ^C break */
                            p_buf[0] = '\0';
                            return (-1);
                        
                        case 0x08:   /* BS  - backspace */
                        case 0x7F:   /* DEL - backspace */
                            if (p != p_buf)
                            {
                                printf("%s",erase_seq);
                                count--;
                                p--;
                            }
                            continue;
                        
                       default:
                        /* Must be a normal character  */
                        if (count < MAX_CMD_LINE_LEN - 1)
                        { 
                            mcu_uart_putc(ch); /* echo input */
                            *p++ = ch;
                            count++;
                        }
                        else
                        {
                            mcu_uart_putc('\a');
                        }
                    }
                }
                
                //enable next receive
                UART_RX_DMA_Status_Clear(); //clear data flag
                UART_RX_DMA_Enable();  //enable next dma date transfer
            }
        }
	}
	
	return 0;
}

/**
  * @brief  parse command line to argv[...].          
  * @note   None        
  * @param  line
  * @retval arg count
  */
int parse_line(char *line, char *argv[])
{
	int nargs = 0;
	
	while (nargs < MAX_CMD_ARG_NUM)
	{
		/* skip any white space */
		while ((' ' == *line) || ( '\t' == *line ))
			++line;
		
		if ('\0' == *line)
		{
			argv[nargs] = NULL;
			return nargs;
		}
		
		argv[nargs++] = line;
		
		/*find end of string*/
		while(*line && (' ' != *line) && ('\t' != *line))
			++line;
		
		if ('\0' == *line)
		{
			argv[nargs] = NULL;
			return nargs;
		}
		
		*line++ = '\0';
	}
	
	return nargs;
}

cmd_tbl_t *find_cmd(const char *cmd)
{
	int len = __lx_mcu_cmd_end - __lx_mcu_cmd_start;
	return find_cmd_tbl(cmd,__lx_mcu_cmd_start, len);
}

int run_command(const char *cmd, int flag)
{
	cmd_tbl_t *cmdtp;
	char cmdbuf[MAX_CMD_LINE_LEN + 1] = {0};
	char *argv[MAX_CMD_ARG_NUM + 1];
	int argc;
	
	int rc = 0;
	
	if (!cmd || !*cmd)
		return -1;
	
	if (strlen(cmd) >= MAX_CMD_LINE_LEN)
	{
		printf("## Command too long\n");
		return -1;
	}
	
	strncpy(cmdbuf,cmd, MAX_CMD_LINE_LEN );
	
	if ((argc = parse_line(cmdbuf, argv)) == 0)
	{
		rc = -1;
		goto ret;
	}
	
	if ((cmdtp = find_cmd(argv[0])) == NULL)
	{
		printf("Unknown command '%s' - try 'help'\n", argv[0]);
		rc = -1;
		goto ret;
	}
	
	if (argc > cmdtp->maxargs)
	{
		cmd_usage(cmdtp);
		rc = -1;
		goto ret;
	}
	
	if ((cmdtp->func)(cmdtp, flag, argc,argv) != 0)
	{
		rc = -1;
		goto ret;
	}
	
ret:
	return rc ;
}


void lx_mcu_cli_main_loop(void)
{
	static char last_command[MAX_CMD_LINE_LEN + 1] = {0};
	int len = 0;
	int flag ;
	int rc = 1;
	
	extern unsigned char Load$$MCU_CMD_LIST$$Base[];
	extern unsigned char Load$$MCU_CMD_LIST$$Limit[];
	
	unsigned long cmd_start = (unsigned long)Load$$MCU_CMD_LIST$$Base;
	unsigned long cmd_end = (unsigned long)Load$$MCU_CMD_LIST$$Limit;
	
	__lx_mcu_cmd_start = (cmd_tbl_t *)cmd_start;
	__lx_mcu_cmd_end = (cmd_tbl_t *)cmd_end;
	
#ifdef CLI_DEBUG
	printf("mcu start cmd addr=%p\n",__lx_mcu_cmd_start);
	printf("mcu end cmd addr=%p\n",__lx_mcu_cmd_end);
#endif
	
	for (;;)
	{
		len = lynxi_mcu_readline(LYNXI_MCU_CMD_LINE_PROMPT);
		
		flag = 0; /* assume no special flags for now */
		if (len > 0)
			strncpy(last_command, console_buffer, MAX_CMD_LINE_LEN);
		else if (len == 0)
			flag |= CMD_FLAG_REPEAT;
		
		if (len == -1)
			printf("\n<INTERRUPT>\n");
		else
			rc = run_command(last_command, flag);
		
		if (rc <= 0)
			last_command[0] = '\0';
		
	}
	
}
